package com.tools.controller;

import com.tools.constants.Constant;
import com.tools.utils.AlertUtil;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import org.apache.log4j.Logger;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ConnectController
 *
 * @author < a href=" ">ZhangYang</ a>
 * @version v1.0.01
 * @date 2018-07-25 16:55
 */
public class TemplateController extends BaseController {
  private static final Logger LOG = Logger.getLogger(TemplateController.class);

  @FXML
  private TreeView<String> templateTree;
  @FXML
  private CodeArea templateEdit;
  private Path path;

  private static final String[] KEYWORDS = new String[] {"abstract", "assert", "boolean", "break", "byte", "case",
    "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final",
    "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native",
    "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch",
    "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while"};

  private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b";
  private static final String PAREN_PATTERN = "\\(|\\)";
  private static final String BRACE_PATTERN = "\\{|\\}";
  private static final String BRACKET_PATTERN = "\\[|\\]";
  private static final String SEMICOLON_PATTERN = "\\;";
  private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\"";
  private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/";
  private static final String PARAMETER_PATTERN = "\\$\\{(.*?)\\}";

  private static final Pattern PATTERN = Pattern.compile(
    "(?<KEYWORD>" + KEYWORD_PATTERN + ")" + "|(?<PAREN>" + PAREN_PATTERN + ")" + "|(?<BRACE>" + BRACE_PATTERN + ")"
      + "|(?<BRACKET>" + BRACKET_PATTERN + ")" + "|(?<SEMICOLON>" + SEMICOLON_PATTERN + ")" + "|(?<STRING>"
      + STRING_PATTERN + ")" + "|(?<COMMENT>" + COMMENT_PATTERN + ")"+ "|(?<PARAMETER>" + PARAMETER_PATTERN + ")");

  @Override
  public void initialize(URL location, ResourceBundle resources) {

    templateEdit.setParagraphGraphicFactory(LineNumberFactory.get(templateEdit));
    templateEdit.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(ignore -> templateEdit.setStyleSpans(0, computeHighlighting(templateEdit.getText())));

    initTVTree();
    loadTVTree();
  }

  public void initTVTree() {
    templateTree.setShowRoot(false);
    templateTree.setRoot(new TreeItem<>());
    Callback<TreeView<String>, TreeCell<String>> defaultCellFactory = TextFieldTreeCell.forTreeView();
    templateTree.setCellFactory((TreeView<String> tv) -> {
      TreeCell<String> cell = defaultCellFactory.call(tv);
      cell.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
        int level = templateTree.getTreeItemLevel(cell.getTreeItem());
        if(level==-1){
          return;
        }
        TreeCell<String> treeCell = (TreeCell<String>) event.getSource();
        if (event.getClickCount() == 2) {
          if (level != 1) {
            templateEdit.clear();
            String tableName = (String) treeCell.getTreeItem().getGraphic().getUserData();
            path = Paths.get(tableName);
            try {
              BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
              String str = null;
              String string = "";
              while ((str = reader.readLine()) != null) {
                string += str + "\n";
              }

              templateEdit.replaceText(0, 0, string);
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      });
      return cell;
    });
  }

  public void loadTVTree() {
    try {
      TreeItem<String> rootTreeItem = templateTree.getRoot();
      rootTreeItem.getChildren().clear();

      TreeItem<String> entityItem = new TreeItem<String>();
      entityItem.setValue("Entity");
      ImageView entityImage = new ImageView("image/template.png");
      entityImage.setFitHeight(14);
      entityImage.setFitWidth(14);
      entityImage.setUserData("Entity");
      entityItem.setGraphic(entityImage);
      createCell(entityItem, Constant.FILE_ENTITY);
      rootTreeItem.getChildren().add(entityItem);

      TreeItem<String> daoItem = new TreeItem<String>();
      daoItem.setValue("Dao");
      ImageView daoImage = new ImageView("image/template.png");
      daoImage.setFitHeight(14);
      daoImage.setFitWidth(14);
      daoImage.setUserData("Dao");
      daoItem.setGraphic(daoImage);
      createCell(daoItem, Constant.FILE_DAO);
      rootTreeItem.getChildren().add(daoItem);

      TreeItem<String> serviceItem = new TreeItem<String>();
      serviceItem.setValue("Service");
      ImageView serviceImage = new ImageView("image/template.png");
      serviceImage.setFitHeight(14);
      serviceImage.setFitWidth(14);
      serviceImage.setUserData("Service");
      serviceItem.setGraphic(serviceImage);
      createCell(serviceItem, Constant.FILE_SERVICE);
      rootTreeItem.getChildren().add(serviceItem);

      TreeItem<String> controllerItem = new TreeItem<String>();
      controllerItem.setValue("Controller");
      ImageView controllerImage = new ImageView("image/template.png");
      controllerImage.setFitHeight(14);
      controllerImage.setFitWidth(14);
      controllerImage.setUserData("Controller");
      controllerItem.setGraphic(controllerImage);
      createCell(controllerItem, Constant.FILE_CONTROLLER);
      rootTreeItem.getChildren().add(controllerItem);

    } catch (Exception e) {
      AlertUtil.showErrorAlert(e.getMessage());
      LOG.error(e.getMessage());
    }

  }

  private void createCell(TreeItem<String> entityItem, String type) {
    String[] e = Paths.get("./template/" + type.toLowerCase()).toFile().list();
    for (int i = 0; i < e.length; i++) {
      ObservableList<TreeItem<String>> children = entityItem.getChildren();
      TreeItem<String> newTreeItem = new TreeItem<>();
      ImageView imageView = new ImageView("image/table.png");
      imageView.setFitHeight(14);
      imageView.setFitWidth(14);
      imageView.setUserData("./template/" + type.toLowerCase() + "/" + e[i]);
      newTreeItem.setGraphic(imageView);
      newTreeItem.setValue(e[i]);
      children.add(newTreeItem);
    }

  }

  public void saveButton() {
    if(null==path){
      return;
    }
    String text = templateEdit.getText();
    try {
      BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
      writer.write(text);
      writer.flush();
      writer.close();
      AlertUtil.showInfoAlert("模版更新成功！");
    } catch (IOException e) {
      AlertUtil.showErrorAlert("模版更新失败："+e.getMessage());
      e.printStackTrace();
    }
  }
  private static StyleSpans<Collection<String>> computeHighlighting(String text) {
    Matcher matcher = PATTERN.matcher(text);
    int lastKwEnd = 0;
    StyleSpansBuilder<Collection<String>> spansBuilder
      = new StyleSpansBuilder<>();
    while(matcher.find()) {
      String styleClass =
        matcher.group("KEYWORD") != null ? "keyword" :
        matcher.group("PAREN") != null ? "paren" :
        matcher.group("BRACE") != null ? "brace" :
        matcher.group("BRACKET") != null ? "bracket" :
        matcher.group("SEMICOLON") != null ? "semicolon" :
        matcher.group("STRING") != null ? "string" :
        matcher.group("PARAMETER") != null ? "parameter" :
        matcher.group("COMMENT") != null ? "comment" :null
        ;
      assert styleClass != null;
      spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
      spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
      lastKwEnd = matcher.end();
    }
    spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
    return spansBuilder.create();
  }

}
